﻿//////////////////////////////////////////////////////////////////////////////////////////////////////
//Name: Bartosz
//FullName: Adamczewski
//Project: InternalClasses
//Component:
//StartDate: 2009-08-17 19:29:04
//EndDate: 2009-08-17 19:29:04
//Rights:  Copyright (c) Bartosz Adamczewski
/////////////////////////////////////////////////////////////////////////////////////////////////////

#region imports.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using InternalClasses.Dynamic;
using System.Reflection;
using System.Collections;
using InternalClasses.Context.Actions;

#endregion

namespace InternalClasses.Context
{
    /// <summary>
    /// The injection context class that serves as a standard entry point to register 
    /// and pull out new modified (injected) types. The context makes use of the attributes
    /// to inject propper methods.
    /// </summary>
    public class InjectionContext : IContext
    {
        private Hashtable contextData;

        /// <summary>
        /// Initializes a new instance of the <see cref="InjectionContext"/> class.
        /// </summary>
        public InjectionContext()
        {
            contextData = new Hashtable();
        }

        #region IContext Members

        /// <summary>
        /// Registers the object.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</typeparam>
        /// <param name="injectedType">Type of the injected.</param>
        public void Register<TClass, TInterface>()
        {
            Type injectedClass = typeof(TClass);
            Type interfaceType = typeof(TInterface);

            MethodInfo[] methods = injectedClass.GetMethods();

            DynamicType builder = new DynamicType();
            builder.DefineType(injectedClass, interfaceType);

            foreach (MethodInfo classMethod in methods)
            {
                if (interfaceType.GetMethod(classMethod.Name, classMethod.GetParameters().Select(p => p.ParameterType).ToArray()) != null)
                {
                    builder.OpenMethodForCreation(classMethod);

                    MethodInjectionAction[] actions = (MethodInjectionAction[])classMethod.GetCustomAttributes(typeof(MethodInjectionAction), false);

                    foreach (var action in actions)
                    {
                        builder.AddInjectedMethod(action.Method, action.InjectionType, action.Parameters);
                    }

                    builder.CreateMethod();
                }
            }

            object injectedType = builder.CreateType();

            lock (contextData)
            {
                contextData[typeof(TClass).FullName + typeof(TInterface).FullName] = injectedType;
            }

        }

        /// <summary>
        /// Registers the specified injected type.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</typeparam>
        /// <param name="injectedType">Type of the injected.</param>
        public void Register<TClass, TInterface>(object injectedType)
        {
            lock (contextData)
            {
                contextData[typeof(TClass).FullName + typeof(TInterface).FullName] = injectedType;
            }
        }

        /// <summary>
        /// Gets from context.
        /// Pulls the object from the local context data.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</typeparam>
        /// <returns></returns>
        public TInterface GetFromContext<TClass, TInterface>() where TInterface : class
        {
            return GetFromContext<TClass, TInterface>(null);
        }

        /// <summary>
        /// Gets from context.
        /// Pulls the object from the local context data.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</typeparam>
        /// <returns></returns>
        public TInterface GetFromContext<TClass, TInterface>(params object[] parameters) where TInterface : class
        {
            string key = typeof(TClass).FullName + typeof(TInterface).FullName;

            if (contextData.ContainsKey(key))
            {
                Type injectedType = (Type)contextData[key];
                object contextObject = Activator.CreateInstance(injectedType, parameters) as TInterface;
                return contextObject as TInterface;
            }

            return null;
        }

        #endregion
    }
}
